package com.jeebey.mybatis.interceptor;

import java.util.Iterator;
import java.util.List;
import java.util.Properties;

import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.CachingExecutor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.SimpleExecutor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import com.jeebey.mybatis.dialect.DialectFactory;
import com.jeebey.mybatis.query.Criterion;
import com.jeebey.mybatis.query.Parameter;
import com.jeebey.mybatis.utils.MappedStatmentUtil;
import com.jeebey.mybatis.utils.SqlGenerateUtil;
/**普通 条件查询*/
@Intercepts({
	@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
	@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
public class CriterionInterceptor implements Interceptor {

	public Object intercept(Invocation invocation) throws Throwable {
		Object targetObject = invocation.getTarget();
		MetaObject executorMetaObject = MetaObject.forObject(targetObject, new DefaultObjectFactory(),
				new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
		while (executorMetaObject.hasGetter("h") || executorMetaObject.hasGetter("target")) {
			Object _hTargetObj = executorMetaObject.hasGetter("h") ? executorMetaObject.getValue("h") : null;
			Object _tTargetObj = executorMetaObject.hasGetter("target") ? executorMetaObject.getValue("target") : null;
			targetObject = _hTargetObj != null ? _hTargetObj : _tTargetObj;
			executorMetaObject = MetaObject.forObject(targetObject, new DefaultObjectFactory(),
					new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
		}

		if (targetObject instanceof CachingExecutor|| targetObject instanceof SimpleExecutor) {
			final MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
			final Object parameterObject = invocation.getArgs()[1];
			String sourceSql = mappedStatement.getBoundSql(parameterObject).getSql();
			
			if(!sourceSql.startsWith("Generic")){
				Criterion criterion=null;
				if ((parameterObject instanceof Criterion)) {
					criterion = (Criterion) parameterObject;
				} else if (parameterObject instanceof MapperMethod.ParamMap) {
					MapperMethod.ParamMap<?> parameterMap = (MapperMethod.ParamMap<?>) parameterObject;
					Iterator<?> itor = parameterMap.values().iterator();
					while (itor.hasNext()) {
						Object obj = itor.next();
						if (obj instanceof Criterion) {
							criterion = (Criterion) obj;
							break;
						}
					}
				}
				
				if (criterion !=null){
					if(parameterObject instanceof Criterion){
						String excuseSQL = SqlGenerateUtil.createSqlSelectCriterion(sourceSql, parameterObject);
						List<Parameter>parameters = SqlGenerateUtil.createParamsSelectCriterion(parameterObject);
						invocation.getArgs()[0] = MappedStatmentUtil.createCriterionMappedStatement(mappedStatement, excuseSQL, parameterObject, parameters,DialectFactory.CURR_DIALECT_IMPL);
					}
					if (parameterObject instanceof MapperMethod.ParamMap){
						String excuseSQL = SqlGenerateUtil.createSqlSelectCriterionPaging(sourceSql, parameterObject);
						List<Parameter>parameters = SqlGenerateUtil.createParamsSelectCriterionPaging(parameterObject);
						invocation.getArgs()[0] = MappedStatmentUtil.createCriterionMappedStatement(mappedStatement, excuseSQL, parameterObject, parameters,DialectFactory.CURR_DIALECT_IMPL);
					}
				}
				
			}
		}
		return invocation.proceed();
	}

	public Object plugin(Object object) {
		return Plugin.wrap(object, this);
	}

	public void setProperties(Properties properties) {
		
	}

}
